// readSpectrumFile.h

// Library of functions used to read spectral data from a plain text file. 
// All files must be in unix format before using any of the following functions.

// Read file routine original written by Gary Hoffman.
// Modifications made by Garreth Ruane.

#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <cmath>
#include <cstdio>
using namespace std;


/* ------------------------------------------------------------------------------
List of file importing functions:

 countChannels - counts the number of channels (wavelengths / data points) in an input file. 
 
 readFile - Saves the data present in the file to wavelengths[], data[], and error[]. 
 
 
Definition of parameters: 
 
 char *filename - LOCATION of the input file. 
 
 float wavelength[] - The array in which wavelengths will be held.
 
 float data[] - The array in which input data (e.g. reflectance) will be held.
 
 float error[] - The array in which the error at each channel will be held. 
 
------------------------------------------------------------------------------*/ 



// ******************* Function Prototypes *******************
// function: countChannels
// counts the number of channels (wavelengths / data points) in an input file. 
int countChannels(char *filename);

// function: readFile
// Saves the data present in the file to wavelengths[], data[], and error[]. 
void readFile( char *filename, double wavelength[], double data[], double error[], int numberOfChannels );


// ******************* Function Definitions *******************


// Function to count the number of channels in an input file.
// i.e. number of wavelengths and data points

int countChannels(char *filename)
{
	ifstream fs;
	fs.open(filename);
    char ch;				//single character storage
    const int MIN_NUMBER_OF_CHANNELS = 10;
    int lineCount = 0;		//counter for number of lines output
    int trailingCount = -1;	//book keeping variable to make sure the program doesnt get stuck on something weird. 


    //if this fails, print error message and return
    if( !fs )
    {    cout << "Error opening file" << endl;
         return 0;
    }

    //otherwise while std input is successful, process data
    else
    {
        while( fs.good() && !fs.eof() && fs )
        {       
         	//examine first non-white space character
   		 	fs >> ch;
   		 	
			//if the character stored in ch is not a digit
			//then ignore the rest of the line
			if( (ch < '0') || (ch > '9') )
			{
				while ((ch != '\n') && (ch != '\r') && (ch != '\x0d'))
					ch = fs.get();
			}
			else
			{
				// ignore not digit characters in between.. like delimeters.
				while((ch >= '0') && (ch <= '9') )
					ch = (char)fs.get();

				// check in next non-white space character.
				fs >> ch;
				// if we're still good, count it
				if( (ch >= '0') && (ch <= '9') )
				{
					lineCount++;
				}
				//ignore the rest of the line
				while ((ch != '\n') && (ch != '\r') && (ch != '\x0d'))
						ch = fs.get();

			} 
			// If a file has bad EOF characters the counter can get stuck in a infinite loop at the end of a file. Here we assume that after reading MIN_NUMBER_OF_CHANNELS lines, the data has started. 
			if( lineCount <= trailingCount && lineCount > MIN_NUMBER_OF_CHANNELS)
			{
				return lineCount;
			}		
			trailingCount = lineCount;
		}
	}
	//if no lines were output, something probably is wrong
	if ( lineCount == 0 )
	{   
		cout << "Error reading file: no data read" << endl;
	    return 0;
	} 
	return lineCount;
}


void readFile( char *filename, double wavelength[], double data[], double error[], int numberOfChannels )
{
    //declare variables
    char ch;			//single character storage
    int index = 0;		// Data line index
    int k = 0;			// Counts number of 4 columns line as a check.
    					// In the end this should be the same as numberOfChannels
    double w1 = 0, d1 = 0;// This first data point storage for wavelength vs wavenumber check. 
    
    // Open the input file stream 
	ifstream fs;
	fs.open(filename);
	
    //examine first non-white space character
    fs >> ch;
	
    //if this fails, print error message and return
    if( !fs )
    {    cout << "Error reading file" << endl;
         return;
    } 

    //otherwise while std input is successful, process data
    else
    {
        while( fs.good() && !fs.eof() && fs && (index < numberOfChannels))
        {    
			//if the character stored in ch is not a digit
			//then put it back in the input stream and 
			//ignore the rest of the line
			if( (ch < '0') || (ch > '9') && ch != '.')
			{    
				fs.putback( ch );
				while ((ch != '\n') && (ch != '\r') )
					ch = fs.get();		
				fs.putback( ch );
			}
			
			else
			{
				//otherwise put the character back in the input stream and read data into wavelength. Look for the next number. 
				fs.putback( ch );
				fs >> wavelength[index];
				
				// Check in next non-white space char
				fs >> ch;
				
				// handle delimeters.
				while (ch == ',' || ch == ';' )
					fs >> ch;

				// Check if data value is there. 
				// If not, ignore the rest of the line
				if( ((ch < '0') || (ch > '9')) && ch != '-' )
				{
					fs.putback( ch );
					while ((ch != '\n') && (ch != '\r'))
						ch = fs.get();
					fs.putback( ch );
				}
				// If data is there go ahead and check it in.
				else
				{
					fs.putback( ch );
					fs >> data[index];		
					if(index == 0)
					{
						w1 = wavelength[0];
						d1 = data[0];
					}
					//determine if additional data are present
					//if the next character is not newline then assume
					//there is another column of data representing error
					ch = fs.get();
		
					//check for white space at end of line
					if( ch == ',' || ch == ';' || ch == ' ' )
					{
						while (ch == ',' || ch == ';' || ch == ' ')
							ch = fs.get();
					}
					//put back the last character
					fs.putback( ch );

					if( ch != '\n' && ch != '\r')
						fs >> error[index];


					// Check if the first column may be a wavenumber column
					// and then check if there is a fourth column,
					// otherwise dont even look. 
					if(fabs(wavelength[index] * data[index] / 10000 - 1) < 0.00001 || fabs( wavelength[index] * data[index] / 10000000 - 1 ) < 0.00001 || fabs( wavelength[index] * data[index] / 100000000 - 1 ) < 0.00001)
					{
						ch = fs.get();
		
						//check for white space at end of line
						if( ch == ',' || ch == ';' || ch == ' ' )
						{
							while (ch == ',' || ch == ';' || ch == ' ')
								ch = fs.get();
						}
						//put back the last character
						fs.putback( ch );

						if( ch != '\n' && ch != '\r')
						{
							if(w1 > d1)
								wavelength[index] = data[index];
							data[index] = error[index];
							fs >> error[index];
							k++;
						}
						else
						{
							fs.putback(ch);
						}
					}// if

					//ignore the rest of the line
					while ((ch != '\n') && (ch != '\r'))
						ch = fs.get();
					fs.putback(ch);
					
					index++;	
					
				}//else
				
			} //else

			//examine first non-white space character of next line
			fs >> ch;
			
		} //while
		if(k != 0 && k != numberOfChannels)
			cout << "File Read Error: inconsistant number of columns?" << endl;
		if(wavelength[1] <= 0)
			cout << "File Read Error: comment out header lines that start with numbers." << endl;
    } //else
}
